package cn.com.idmy.cloud.advice;

import cn.com.idmy.base.exception.BizException;
import cn.com.idmy.base.exception.UnauthorizedException;
import cn.com.idmy.base.model.Out;
import cn.com.idmy.base.util.AppUtil;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hutool.core.exception.ExceptionUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.jetbrains.annotations.NotNull;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.util.Objects;

@Slf4j
@RestControllerAdvice
public class GlobalResponseHandler extends ResponseEntityExceptionHandler {
    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(@NotNull MethodArgumentNotValidException ex, @NotNull HttpHeaders headers, @NotNull HttpStatusCode status, @NotNull WebRequest request) {
        var err = Objects.requireNonNull(ex.getBindingResult().getFieldError());
        var msg = err.getDefaultMessage();
        var field = err.getField();
        return new ResponseEntity<>(Out.err().msg(field + ": " + msg), status);
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<Out> handler(@NotNull IllegalArgumentException e) {
        log.error(e.getMessage(), e);
        return new ResponseEntity<>(Out.err().msg(e.getMessage()).data(AppUtil.isLocal() ? e.getStackTrace() : null), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(UnauthorizedException.class)
    public ResponseEntity<Out> handler(@NotNull UnauthorizedException e) {
        var status = HttpStatus.UNAUTHORIZED;
        var msg = e.getMessage();
        log.error(msg);
        return new ResponseEntity<>(Out.err().msg(msg).code(e.code()).data(AppUtil.isLocal() ? e.getStackTrace() : null), status);
    }

    @ExceptionHandler(BizException.class)
    public ResponseEntity<Out> handler(@NotNull BizException e) {
        var status = HttpStatus.resolve(e.code());
        if (status == null) {
            status = HttpStatus.INTERNAL_SERVER_ERROR;
        }
        var msg = e.getMessage();
        if (StrUtil.isNotBlank(msg)) {
            msg = msg.replace(BizException.class.getSimpleName() + ": ", "");
        }
        log.error(msg, e);
        return new ResponseEntity<>(Out.err().msg(msg).code(e.code()).data(AppUtil.isLocal() ? e.getStackTrace() : null), status);
    }

    @ExceptionHandler(NullPointerException.class)
    public ResponseEntity<Out> handler(@NotNull NullPointerException e) {
        var msg = e.getMessage();
        if (msg != null && msg.contains("non-null")) {
            msg = msg.replace("is marked non-null but is null", "不能为空");
        }
        log.error(NullPointerException.class.getSimpleName(), e);
        return new ResponseEntity<>(Out.err().msg(msg), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(DuplicateKeyException.class)
    public ResponseEntity<Out> handler(@NotNull DuplicateKeyException e) {
        log.error(e.getMessage(), e);
        return new ResponseEntity<>(Out.err().msg("存在重复数据"), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(DataIntegrityViolationException.class)
    public ResponseEntity<Out> handler(@NotNull DataIntegrityViolationException e) {
        var msg = ExceptionUtil.getRootCauseMessage(e);
        if (msg != null && msg.contains("doesn't have a default value")) {
            msg = msg.replace("SQLException: Field", "").replace("doesn't have a default value", "");
            msg = "数据库字段【" + msg + "】没有默认值";
        }
        return new ResponseEntity<>(Out.err().msg(msg).data(AppUtil.isLocal() ? e.getStackTrace() : null), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(Throwable.class)
    public ResponseEntity<Out> handler(@NotNull Throwable e) {
        e = ExceptionUtil.getRootCause(e);
        log.error(Throwable.class.getSimpleName(), e);
        return new ResponseEntity<>(Out.err().msg(e.getMessage()).data(AppUtil.isLocal() ? e.getStackTrace() : null), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}
